﻿using System;
using Nofs.Net.Common.Interfaces.Library;
using Nofs.Net.Execution;
using Nofs.Net.Common.Interfaces.Factories;
using Nofs.Net.nofs.metadata.interfaces;
using Nofs.Net.Common.Interfaces.Cache;
using Nofs.Net.Common.Interfaces.Domain;
using Nofs.Net.Cache.Impl;
using Nofs.Net.Domain.Impl;
using biz.ritter.javapi.util;
using Nofs.Fuse;
using biz.ritter.javapi.nio;
using biz.ritter.javapi.lang;

namespace Nofs.Net.Fuse.Impl
{

    public class NoFSFuseDriver : INoFSFuseDriver
    {

        private IStatHandler _stat;
        private IFsyncHandler _fsync;
        private IDirHandler _dir;
        private IFileHandler _file;
        private IFileDataHandler _data;
        private ILinkHandler _link;
        private IStatMapper _statMapper;
        private IExtendedAttributeHandler _xattr;

        private IDomainObjectContainerManagerInternal _containerManager;
        private PathTranslator _translator;
        private LogManager _logger;
        private ExecutionServer _server;

        public IDomainObjectContainerManagerInternal GetDBForTesting()
        {
            return _containerManager;
        }

        public NoFSFuseDriver(
                String domainFile, String objectStore,
                String metaLocation, String metadataFactoryClassName,
                String persistenceFactoryClassName) //throws Exception
            : this(domainFile, objectStore, metaLocation,
                    (IMetadataFactory)Class.forName(metadataFactoryClassName).newInstance(),
                    (IPersistenceFactory)Class.forName(persistenceFactoryClassName).newInstance())
        {
           
        }

        public NoFSFuseDriver(
                String domainFile, 
                String objectStore,
                String metaLocation, 
                IMetadataFactory factory, 
                IPersistenceFactory persistenceFactory)// throws Exception {
            : this(factory.CreateClassLoader(domainFile), objectStore, metaLocation, factory, persistenceFactory)
        {
        }

        public NoFSFuseDriver(
                INoFSClassLoader classLoader, 
                String objectStore,
                String metaLocation, 
                IMetadataFactory factory, 
                IPersistenceFactory persistenceFactory) //throws Exception 
        {
            IAttributeAccessor attributeAccessor = factory.CreateAttributeAccessor();
            LockManager lockManager = new LockManager();
            LogManager logManager = new LogManager();

            IKeyCache keyCache = persistenceFactory.CreateKeyCache(objectStore, metaLocation, logManager);
            _statMapper = persistenceFactory.CreateStatMapper(objectStore, metaLocation, typeof(FileObjectStat).getClass(), logManager);
            IFileObjectFactory fileObjectFactory = new FileObjectFactory(attributeAccessor, _statMapper, keyCache);
           
            //
            IDomainObjectContainerManagerInternal containerManager = (IDomainObjectContainerManagerInternal)persistenceFactory.CreateContainerManager
                    (
                    objectStore, 
                    metaLocation, 
                    logManager, 
                    _statMapper, 
                    keyCache, 
                    fileObjectFactory, 
                    attributeAccessor);

            PathTranslator pathTranslator = new PathTranslator
                        (
                        classLoader,
                         (IDomainObjectContainerManager)containerManager, 
                        fileObjectFactory
                        );
            IFileCacheManager fileCacheManager = new FileCacheManager
                    (
                    (IDomainObjectContainerManager)containerManager  , 
                    logManager, 
                    factory.CreateMethodFilter()
                    );

            containerManager.SetFileCacheManager(fileCacheManager);
            DomainObjectCollectionHelper collectionHelper = new DomainObjectCollectionHelper(
                (IDomainObjectContainerManager)containerManager, 
                attributeAccessor);

            _stat = new StatHandler(fileCacheManager, pathTranslator, lockManager, _statMapper, logManager);
            _xattr = new ExtendedAttributeHandler(pathTranslator, lockManager, _statMapper, logManager);
            _fsync = new FsyncHandler();
            _dir = new DirHandler(pathTranslator, collectionHelper, lockManager, logManager);
            _file = new FileHandler(pathTranslator, collectionHelper, lockManager, logManager);
            _data = new FileDataHandler(fileCacheManager, pathTranslator, lockManager, logManager);
            _containerManager = containerManager;
            _translator = pathTranslator;
            _logger = logManager;
            _link = new LinkHandler();
            _server = new ExecutionServer(_translator, _logger);
        }

        public void DumpStatTables() //throws Exception 
        {
            _statMapper.DumpStatTables();
        }

        //@SuppressWarnings("unchecked")
        public LinkedList<Object> GetFSObjectsByType(String className) //throws Exception 
        {
            LinkedList<Object> objs = new LinkedList<Object>();
            var container = _containerManager.GetContainer(className);
            foreach (Object obj in container.GetAllInstances())
            {
                objs.add(obj);
            }
            return objs;
        }

        public void Init() //throws Exception 
        {
            if (!_translator.FileSystemHasARoot())
            {
                _logger.LogInfo("No root found in file system. Attempting to create one...");
                _translator.CreateRootInFileSystem(_logger);
            }
            else
            {
                _logger.LogInfo("File system root object found.");
            }
            _logger.LogInfo("starting execution server...");
            _server.Start();
            _logger.LogInfo("execution server started");
        }

        //@Override
        public void CleanUp() //throws Exception 
        {
            _logger.LogInfo("stopping execution server...");
            _server.Stop();
            _logger.LogInfo("execution server stopped");
            _containerManager.CleanUp();
        }

        //@Override
        public int chmod(String path, int mode) //throws FuseException 
        {
            return _stat.chmod(path, mode);
        }

        //@Override
        public int chown(String path, int uid, int gid)// throws FuseException
        {
            return _stat.chown(path, uid, gid);
        }

        //@Override
        public int flush(String path, Object fileHandle) //throws FuseException
        {
            return _data.flush(path, fileHandle);
        }

        //@Override
        public int fsync(String path, Object fileHandle, bool isDataSync) //throws FuseException 
        {
            return _fsync.fsync(path, fileHandle, isDataSync);
        }

        //@Override
        public int getattr(String path, FuseGetattrSetter attr) //throws FuseException 
        {
            return _stat.getattr(path, attr);
        }

        //@Override
        public int getdir(String path, FuseDirFiller filler) //throws FuseException 
        {
            return _dir.getdir(path, filler);
        }

        //@Override
        public int link(String fromPath, String toPath) //throws FuseException 
        {
            return FuseErrno.ENOTSUPP;
        }

        //@Override
        public int mkdir(String path, int mode) //throws FuseException
        {
            return _dir.mkdir(path, mode);
        }

        //@Override
        public int mknod(String path, int mode, int rdev) //throws FuseException 
        {
            return _file.mknod(path, mode, rdev);
        }

        //@Override
        public int open(String path, int flags, FuseOpenSetter openSetter) //throws FuseException 
        {
            return _data.open(path, flags, openSetter);
        }

        //@Override
        public int read(String path, Object fh, ByteBuffer buf, long offset) //throws FuseException 
        {
            return _data.read(path, fh, buf, offset);
        }

        //@Override
        public int readlink(String path, CharBuffer link)// throws FuseException
        {
            return _link.readlink(path, link);
        }

        //@Override
        public int release(String path, Object fh, int flags) //throws FuseException 
        {
            return _data.release(path, fh, flags);
        }

        //@Override
        public int rename(String from, String to)// throws FuseException 
        {
            return _file.rename(from, to);
        }

        //@Override
        public int rmdir(String path) //throws FuseException 
        {
            return _dir.rmdir(path);
        }

        //@Override
        public int statfs(FuseStatfsSetter statfsSetter)// throws FuseException 
        {
            int stat = 0;
            try
            {
                statfsSetter.set(2048, 
                                1024 * 1024 * 1024, 
                                1024 * 1024 * 1024,
                                1024 * 1024 * 1024,
                                1000,
                                1000 + 1024 * 1024,
                                2048);
            }
            catch (System.Exception e)
            {
                throw new FuseException("statfs()", e);
            }
            return stat;
        }

        //@Override
        public int symlink(String linkTarget, String linkPath)// throws FuseException 
        {
            return _link.symlink(linkTarget, linkPath);
        }

        //@Override
        public int truncate(String path, long length) //throws FuseException 
        {
            return _data.truncate(path, length);
        }

        //@Override
        public int unlink(String path)// throws FuseException 
        {
            return _file.unlink(path);
        }

        //@Override
        public int utime(String path, int atime, int mtime) //throws FuseException 
        {
            return _stat.utime(path, atime, mtime);
        }

        //@Override
        public int write(String path, Object fh, bool isWritePage, ByteBuffer buf, long offset)// throws FuseException
        {
            return _data.write(path, fh, isWritePage, buf, offset);
        }

        public int getxattr(String path, String name, ByteBuffer dst) //throws FuseException, BufferOverflowException 
        {
            return _xattr.getxattr(path, name, dst);
        }

        public int getxattrsize(String path, String name, FuseSizeSetter sizeSetter) //throws FuseException 
        {
            return _xattr.getxattrsize(path, name, sizeSetter);
        }

        public int listxattr(String path, XattrLister lister)// throws FuseException 
        {
            return _xattr.listxattr(path, lister);
        }

        public int removexattr(String path, String name) //throws FuseException
        {
            return _xattr.removexattr(path, name);
        }

        public int setxattr(String path, String name, ByteBuffer value, int flags) //throws FuseException
        {
            return _xattr.setxattr(path, name, value, flags);
        }

    }

}
